<template>
  <div>
    <MenuBar
      v-if="showContextMenu && operate === 'deploy'"
      ref="menuBar"
      @callBack="contextMenuFn"
    />
    <div class="servicePackageListDiv">
      <el-tooltip class="item" effect="dark" content="部署" placement="bottom">
        <el-button
          v-show="operate === 'deploy'"
          type="primary"
          size="small"
          @click="deployServiceGroup()"
          class="el-button-primary-style"
        >
          部 署
        </el-button>
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="返回" placement="bottom">
        <el-button
          type="primary"
          size="small"
          style="margin-right: 20px"
          @click="goServiceGroupPage"
          class="el-button-primary-style"
        >
          返 回
        </el-button>
      </el-tooltip>
    </div>
    <div id="container" class="graphDiv" @click.stop="hideFn"/>
    <service-group-deploy-one-service-dialog
      ref="serviceDeployDialog"
      :app-name="selectAppName"
    />
    <div v-if="showExistInstanceDialogDiv">
      <select-exist-instance-dialog
        ref="selectExistInstance"
        :app-name="selectAppName"
      />
    </div>
    <el-dialog
      title="服务组部署"
      :visible.sync="deployDialogVisible"
      width="35%"
      :destroy-on-close="true"
      center
    >
      <div style="display: flex; justify-content: space-around">
        <div>
          <span>服务组实例名称: </span>
          <el-input
            v-model="gvStoreGroupDeployVo.groupName"
            style="width: 250px"
            clearable
          />
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" size="small" @click="startDeploy"
                   class="el-button-primary-style"
        >
          确 定
        </el-button>
        <el-button size="small" @click="clearDialogInfo">取 消</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  import {Graph, Path} from "@antv/x6";
  import onlyin from "@/views/appstore/components/ServiceGroupComponents/nodeTheme/onlyIn.vue";
  import database from "@/views/appstore/components/ServiceGroupComponents/nodeTheme/database.vue";
  import "@antv/x6-vue-shape";
  import MenuBar from "@/views/appstore/components/ServiceGroupComponents/MenuBar.vue";
  import ServiceGroupDeployOneServiceDialog
    from "@/views/appstore/components/ServiceGroupDeployComponents/ServiceGroupDeployOneServiceDialog.vue";
  import SelectExistInstanceDialog
    from "@/views/appstore/components/ServiceGroupDeployComponents/SelectExistInstanceDialog.vue";
  import {getTenantId} from "@/utils/tenantId";

  export default {
    name: "ServiceGroupDeploy",
    components: {
      SelectExistInstanceDialog,
      ServiceGroupDeployOneServiceDialog,
      MenuBar,
    },
    data() {
      return {
        graph: "",
        showContextMenu: false,
        serviceGroupInstance: {},
        operate: "",
        selectAppName: "",
        gvStoreGroupDeployVo: {
          tenantId: "",
          groupName: "",
          groupId: "",
          nodes: [],
        },
        nodeInfo: {
          deployId: "",
          appName: "",
          appVersion: "",
          depends: [],
          useAppliedDeploy: false,
          yamlDeploy: {},
        },
        deployDialogVisible: false,
        showExistInstanceDialogDiv: false,
      };
    },
    beforeMount() {
      if (this.$route.params.serviceGroupInfo === undefined) {
        this.$router.push({
          name: "ServiceGroup",
        });
      } else {
        this.serviceGroupInstance = this.$route.params.serviceGroupInfo;
        this.operate = this.$route.params.operate;
        this.$appStoreApi
          .getGroupRelationGraph(this.serviceGroupInstance.id)
          .then((res) => {
            if (res && res.code === 200) {
              this.serviceGroupInstance.relationGraph = res.data;
              this.initGraph();
              this.loadFn();
            }
          });
      }
    },
    methods: {
      hideFn() {
        this.showContextMenu = false;
      },
      initGraph() {
        // 注册节点
        Graph.registerNode(
          "dag-onlyIn",
          {
            inherit: "vue-shape",
            width: 180,
            height: 36,
            component: {
              template: `
                <onlyin/>`,
              components: {
                onlyin,
              },
            },
            ports: {
              groups: {
                top: {
                  position: "top",
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: "#C2C8D5",
                      strokeWidth: 1,
                      fill: "#fff",
                    },
                  },
                },
                bottom: {
                  position: "bottom",
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: "#C2C8D5",
                      strokeWidth: 1,
                      fill: "#fff",
                    },
                  },
                },
              },
            },
          },
          true
        );
        Graph.registerNode(
          "dag-node",
          {
            inherit: "vue-shape",
            width: 180,
            height: 36,
            component: {
              template: `
                <database/>`,
              components: {
                database,
              },
            },
            ports: {
              groups: {
                top: {
                  position: "top",
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: "#C2C8D5",
                      strokeWidth: 1,
                      fill: "#fff",
                    },
                  },
                },
                bottom: {
                  position: "bottom",
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: "#C2C8D5",
                      strokeWidth: 1,
                      fill: "#fff",
                    },
                  },
                },
              },
            },
          },
          true
        );

        Graph.registerEdge(
          "dag-edge",
          {
            inherit: "edge",
            attrs: {
              line: {
                stroke: "#C2C8D5",
                strokeWidth: 2,
                targetMarker: {
                  name: "block",
                  width: 12,
                  height: 8,
                },
              },
            },
          },
          true
        );
        Graph.registerConnector(
          "algo-connector",
          (s, e) => {
            const offset = 4;
            const deltaY = Math.abs(e.y - s.y);
            const control = Math.floor((deltaY / 3) * 2);

            const v1 = {x: s.x, y: s.y + offset + control};
            const v2 = {x: e.x, y: e.y - offset - control};

            return Path.normalize(
              `M ${s.x} ${s.y}
           L ${s.x} ${s.y + offset}
           C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
           L ${e.x} ${e.y}
          `
            );
          },
          true
        );

        const graph = new Graph({
          grid: {
            size: 10,
            visible: true,
            type: "dot", // 'dot' | 'fixedDot' | 'mesh'
            args: {
              color: "#a05410", // 网格线/点颜色
              thickness: 1, // 网格线宽度/网格点大小
            },
          },
          background: {
            color: "#ededed", // 设置画布背景颜色
          },
          container: document.getElementById("container"),
          panning: {
            // enabled: true,
            eventTypes: ["leftMouseDown", "mouseWheel"],
          },
          // mousewheel: {
          //   enabled: true,
          //   modifiers: "ctrl",
          //   factor: 1.1,
          //   maxScale: 1.5,
          //   minScale: 0.5,
          // },
          highlighting: {
            magnetAdsorbed: {
              name: "stroke",
              args: {
                attrs: {
                  fill: "#fff",
                  stroke: "#31d0c6",
                  strokeWidth: 4,
                },
              },
            },
          },
          connecting: {
            snap: true,
            allowBlank: false,
            allowLoop: false,
            highlight: true,
            connector: "algo-connector",
            connectionPoint: "anchor",
            anchor: "center",
            validateMagnet() {
              // return magnet.getAttribute('port-group') !== 'top'

              // 限制连线配置
              return true;
            },
            createEdge() {
              return graph.createEdge({
                shape: "dag-edge",
                attrs: {
                  line: {
                    strokeDasharray: "5 5",
                    targetMarker: {
                      name: "block",
                      width: 12,
                      height: 8,
                    },
                  },
                },
                zIndex: -1,
              });
            },
          },
          selecting: {
            enabled: true,
            multiple: true,
            rubberEdge: true,
            rubberNode: true,
            modifiers: "shift",
            rubberband: true,
          },
          keyboard: true,
          clipboard: true,
          history: true,
          height: 800,
        });
        this.graph = graph;

        graph.on("edge:contextmenu", ({e, x, y, edge, view}) => {
          console.log("----", x, y, view);
          this.showContextMenu = true;
          this.$nextTick(() => {
            // this.$refs.menuBar.initFn(e.offsetX, e.offsetY, {
            this.$refs.menuBar.initFn(
              x + 290,
              y + 20,
              {
                type: "edge",
                item: edge,
              },
              "deploy"
            );
          });
        });

        graph.on("node:contextmenu", ({e, x, y, node, view}) => {
          console.log("++++", e, x, y, view);
          this.showContextMenu = true;

          this.$nextTick(() => {
            // this.$refs.menuBar.setItem({ type: 'node', item: node })
            const p = graph.localToPage(x, y);
            this.$refs.menuBar.initFn(
              p.x - 220,
              p.y - 70,
              {type: "node", item: node},
              "deploy"
            );
          });
        });

        graph.on("edge:connected", ({edge}) => {
          const source = graph.getCellById(edge.source.cell);
          const target = graph.getCellById(edge.target.cell);

          // 只允许输入
          if (target.data.type === "output") {
            return graph.removeEdge(edge.id);
          }

          // 只允许输出
          if (source.data.type === "onlyIn") {
            return graph.removeEdge(edge.id);
          }

          // 如果线源头的一端链接桩只允许输入
          if (/in/.test(edge.source.port)) {
            return graph.removeEdge(edge.id);
          }

          // 目标一端链接桩只允许输出
          if (/out/.test(edge.target.port)) {
            return graph.removeEdge(edge.id);
          }

          if (source.data.type === "condition") {
            if (target.data.t === edge.id || target.data.f === edge.id) {
              return graph.removeEdge(edge.id);
            }
            this.$refs.dialogCondition.visible = true;
            this.$refs.dialogCondition.init(source.data, edge);
          }

          edge.attr({
            line: {
              strokeDasharray: "",
            },
          });
        });

        graph.on("node:change:data", ({node}) => {
          const edges = graph.getIncomingEdges(node);
          const {status} = node.getData();
          edges?.forEach((edge) => {
            if (status === "running") {
              edge.attr("line/strokeDasharray", 5);
              edge.attr(
                "line/style/animation",
                "running-line 30s infinite linear"
              );
            } else {
              edge.attr("line/strokeDasharray", "");
              edge.attr("line/style/animation", "");
            }
          });
        });
      },
      loadFn() {
        const x6Json = JSON.parse(this.serviceGroupInstance.relationGraph);
        this.startFn(x6Json.cells);
      },
      startFn(item) {
        this.init(item);
        this.graph.centerContent();
      },
      // 初始化节点/边
      init(data = []) {
        const cells = [];
        data.forEach((item) => {
          if (item.shape === "dag-edge") {
            cells.push(this.graph.createEdge(item));
          } else {
            delete item.component;
            cells.push(this.graph.createNode(item));
          }
        });
        this.graph.resetCells(cells);
      },
      contextMenuFn(type, node) {
        switch (type) {
          case "remove":
            if (node.type === "edge") {
              this.graph.removeEdge(node.item.id);
            } else if (node.type === "node") {
              this.graph.removeNode(node.item.id);
            }
            break;
          case "source":
            this.selectAppName = node.item.store.data.data.name;
            this.$refs.serviceDeployDialog.showOneServiceDeployDialog = true;
            // let filter = this.gvStoreGroupDeployVo.nodes.filter(ele => ele.appName === node.item.store.data.data.name);
            // if (filter){
            //   this.$refs.serviceDeployDialog.gvStoreYamlDeployVo = filter[0].yamlDeploy;
            // }
            this.$nextTick(() => {
              if (this.$refs.serviceDeployDialog !== undefined) {
                this.$refs.serviceDeployDialog.$on(
                  "addServiceGroupOneServiceToGvStoreGroupDeployVo",
                  this.addServiceGroupOneServiceToGvStoreGroupDeployVo
                );
              }
            });
            break;
          case "selectInstance":
            this.selectAppName = node.item.store.data.data.name;
            this.showExistInstanceDialogDiv = true;
            this.$nextTick(() => {
              if (this.$refs.selectExistInstance !== undefined) {
                this.$refs.selectExistInstance.showExistInstanceDialog = true;
                this.$refs.selectExistInstance.$on(
                  "addExistInstance",
                  this.addExistInstanceService
                );
              }
            });
            break;
        }
        this.showContextMenu = false;
      },
      addServiceGroupOneServiceToGvStoreGroupDeployVo(oneServiceDetail) {
        const existingIndex = this.gvStoreGroupDeployVo.nodes.findIndex(
          (item) => item.appName === oneServiceDetail.yamlDeployBaseInfo.appName
        );
        this.nodeInfo.appName = oneServiceDetail.yamlDeployBaseInfo.appName;
        this.nodeInfo.appVersion = oneServiceDetail.yamlDeployBaseInfo.version;
        this.nodeInfo.yamlDeploy = oneServiceDetail;
        if (existingIndex !== -1) {
          // 如果已存在相同的 key，则更新对应的 value
          this.$set(this.gvStoreGroupDeployVo.nodes, existingIndex, {
            ...this.gvStoreGroupDeployVo.nodes[existingIndex],
            ...this.nodeInfo,
          });
          this.$message.success("更新成功");
        } else {
          // 如果不存在相同的 key，则添加新的数据
          this.gvStoreGroupDeployVo.nodes.push({...this.nodeInfo});
          this.$message.success("保存成功");
        }
        this.$refs.serviceDeployDialog.closeServiceConfigDialog();
        this.$refs.serviceDeployDialog.$off(
          "addServiceGroupOneServiceToGvStoreGroupDeployVo"
        );
      },
      deployServiceGroup() {
        const x6Json = JSON.parse(this.serviceGroupInstance.relationGraph);
        const nodeListLength = x6Json.cells.filter(
          (shape) => shape.shape === "dag-node"
        ).length;
        if (this.gvStoreGroupDeployVo.nodes.length !== nodeListLength) {
          this.$message.warning("请将所有节点数据进行绑定后，再进行部署");
          return;
        }
        x6Json.cells.forEach((ele) => {
          let tmpNodeInfo = {
            deployId: "",
            appName: "",
            appVersion: "",
            depends: [],
            useAppliedDeploy: false,
            yamlDeploy: {},
          };
          if (ele.shape === "dag-node") {
            tmpNodeInfo = this.gvStoreGroupDeployVo.nodes.filter(
              (gvStore) => ele.data.name === gvStore.appName
            )[0];
            let edgeForId = [];
            edgeForId = x6Json.cells.filter(
              (shape) =>
                shape.shape === "dag-edge" && shape.source.cell === ele.id
            );
            if (edgeForId.length !== 0) {
              const tmpDeps = [];
              edgeForId.forEach((ed) => {
                let filter = [];
                filter = x6Json.cells.filter(
                  (node) => node.id === ed.target.cell
                );
                filter.forEach((fil) => {
                  const filter1 = this.gvStoreGroupDeployVo.nodes.filter(
                    (e) => e.appName === fil.data.name
                  )[0];
                  const tmpDep = {
                    appName: filter1.appName,
                    appVersion: filter1.appVersion,
                  };
                  tmpDeps.push(tmpDep);
                });
              });
              tmpNodeInfo.depends = tmpDeps;
            }
            const existingIndex = this.gvStoreGroupDeployVo.nodes.findIndex(
              (item) => item.appName === tmpNodeInfo.appName
            );
            if (existingIndex !== -1) {
              this.gvStoreGroupDeployVo.nodes[existingIndex] = Object.assign(
                {},
                this.gvStoreGroupDeployVo.nodes[existingIndex],
                tmpNodeInfo
              );
            }
          }
        });
        this.deployDialogVisible = true;
      },
      addExistInstanceService(serviceInstance, type) {
        if (type === "cancel") {
          this.showExistInstanceDialogDiv = false;
          this.$refs.serviceDeployDialog.$off("addExistInstance");
          return;
        }
        const existingIndex = this.gvStoreGroupDeployVo.nodes.findIndex(
          (item) => item.appName === serviceInstance.appName
        );
        this.nodeInfo.appName = serviceInstance.appName;
        this.nodeInfo.appVersion = serviceInstance.appVersion;
        this.nodeInfo.useAppliedDeploy = true;
        this.nodeInfo.deployId = serviceInstance.id;
        this.nodeInfo.yamlDeploy = {};
        if (existingIndex !== -1) {
          // 如果已存在相同的 key，则更新对应的 value
          this.$set(this.gvStoreGroupDeployVo.nodes, existingIndex, {
            ...this.gvStoreGroupDeployVo.nodes[existingIndex],
            ...this.nodeInfo,
          });
          this.$message.success("更新成功");
        } else {
          // 如果不存在相同的 key，则添加新的数据
          this.gvStoreGroupDeployVo.nodes.push({...this.nodeInfo});
          this.$message.success("保存成功");
        }
        this.showExistInstanceDialogDiv = false;
        this.$refs.serviceDeployDialog.$off("addExistInstance");
      },
      startDeploy() {
        if (this.gvStoreGroupDeployVo.groupName === "") {
          this.$message.warning("请输入服务组实例名称");
          return;
        }
        this.gvStoreGroupDeployVo.groupId = this.serviceGroupInstance.id;
        this.gvStoreGroupDeployVo.tenantId = getTenantId();
        this.$appManageApi
          .createGroupDeploy(this.gvStoreGroupDeployVo)
          .then((res) => {
            if (res && res.code === 200) {
              this.$appManageApi.startGroupDeploy([res.data]).then((res) => {
                if (res && res.code === 200) {
                  this.$message.success("服务组启动成功");
                  this.clearDialogInfo();
                  this.$router.push({
                    name: "ServiceGroupList",
                  });
                }
              });
            }
          });
      },
      clearDialogInfo() {
        this.deployDialogVisible = false;
      },
      goServiceGroupPage() {
        this.$router.push({
          name: "ServiceGroup",
        });
      },
    },
  };
</script>

<style scoped>
  .servicePackageListDiv {
    border: 1px solid #304156;
    height: 40px;
    width: 100%;
    background-color: #304156;
    display: flex;
    /*justify-content: space-around;*/
    justify-content: right;
    align-items: center;
  }

  /*::v-deep .el-button--primary {*/
  /*  background-color: #304156;*/
  /*  border-color: #ededed;*/
  /*}*/
</style>
